package models

import (
	"bigdevops/src/common"
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
)

// 面向运维的k8s项目
type K8sCronjob struct {
	Model        // 不用每次写ID 和 createAt了
	Name  string `json:"name" validate:"required,min=1,max=200" gorm:"uniqueIndex:name_k8s_project_id;type:varchar(100);comment:项目英文集群名称，支持使用通配符*进行模糊搜索" `

	Cluster      string `json:"cluster" gorm:"type:varchar(100);comment:k8s英文集群名称，支持使用通配符*进行模糊搜索" `
	TreeNodeId   uint   `json:"treeNodeId" gorm:"comment:绑定到哪个节点的 面向第三层级"`
	UserID       uint
	K8sProjectID uint `gorm:"uniqueIndex:name_k8s_project_id;"`

	Namespace string `json:"namespace"`

	// k8s 计划任务的字段 https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/
	Schedule string `json:"schedule"`
	Image    string `json:"image"`

	Commands StringArray `json:"commands" gorm:"comment: 启动命令组 [] "`
	Args     StringArray `json:"args" gorm:"comment: 参数空格分割 --cert-dir=/tmp --secure-port=4443"`

	CommandsFront []common.FromKvOneItem `json:"commandsFront" gorm:"-"`
	ArgsFront     []common.FromKvOneItem `json:"argsFront" gorm:"-"`

	// 给前端用的
	// 状态字段
	LastScheduleTime    string      `json:"lastScheduleTime" gorm:"-"`
	LastSchedulePodName string      `json:"lastSchedulePodName" gorm:"-"`
	CreateUserName      string      `json:"createUserName" gorm:"-"`
	NodePath            string      `json:"nodePath" gorm:"-"` // a.b.c.d
	Key                 string      `json:"key" gorm:"-"`      //给前端表格
	StreeNodeObj        *StreeNode  `json:"streeNodeObj" gorm:"-"`
	ClusterObj          *K8sCluster `json:"clusterObj"  gorm:"-"`
	ProjectObj          *K8sProject `json:"projectObj" gorm:"-"`
	K8sProjectName      string      `json:"k8SProjectName" gorm:"-"` // a.b.c.d
}

func DeleteK8sCronjobOneByInstanceId(iid string) error {

	return Db.Select(clause.Associations).Unscoped().Where("instance_id = ?", iid).Delete(&K8sCronjob{}).Error
}

func (obj *K8sCronjob) CreateOne() error {
	return Db.Create(obj).Error

}

func (obj *K8sCronjob) GenCronjob() *batchv1.CronJob {
	cj := &batchv1.CronJob{}
	cj.Namespace = obj.Namespace
	cj.Name = fmt.Sprintf("cronjob-%v-%v", obj.K8sProjectName, obj.Name)
	cj.Spec.Schedule = obj.Schedule

	// 设置标签
	fixLabelM := map[string]string{
		common.K8S_APP_LABEL_STREE_NAME:   obj.StreeNodeObj.NodePath,
		common.K8S_APP_LABEL_PROJECT_NAME: obj.K8sProjectName,
		common.K8S_APP_LABEL_CRONJOB_NAME: obj.Name,
	}
	cj.Labels = fixLabelM

	// 添加容器配置
	c := corev1.Container{
		Name:  obj.Name,
		Image: obj.Image,
	}
	if len(obj.Commands) > 0 {
		c.Command = obj.Commands
	}
	if len(obj.Args) > 0 {
		c.Args = obj.Args
	}
	cj.Spec.JobTemplate.Spec.Template.Spec.Containers = []corev1.Container{c}
	cj.Spec.JobTemplate.Spec.Template.Spec.RestartPolicy = corev1.RestartPolicyOnFailure

	//- key: "nodegroup"
	//  value: "job"
	//  effect: "NoSchedule"
	cj.Spec.JobTemplate.Spec.Template.Spec.Tolerations = []corev1.Toleration{
		{
			Key:    "nodegroup",
			Value:  "job",
			Effect: "NoSchedule",
		},
	}
	// 增加pod的标签辨识度
	cj.Spec.JobTemplate.Spec.Template.Labels = fixLabelM
	//selector := &metav1.LabelSelector{
	//	MatchLabels: fixLabelM,
	//}
	//cj.Spec.JobTemplate.Spec.Selector = selector
	//cj.Spec.JobTemplate.Spec.Template.Spec.Tolerations
	// 计划任务 配置调优
	// 并发规则 设置禁止 ：不然 如果执行时间 大于调度周期 ，会有pod堆积 https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/#concurrency-policy
	cj.Spec.ConcurrencyPolicy = batchv1.ForbidConcurrent
	// 任务历史限制  https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/cron-jobs/#jobs-history-limits
	var s1 *int32
	t1 := int32(2)

	s1 = &t1
	cj.Spec.SuccessfulJobsHistoryLimit = s1
	cj.Spec.FailedJobsHistoryLimit = s1
	return cj
}

func (obj *K8sCronjob) UpdateOne() error {
	return Db.Where("id = ?", obj.ID).Updates(obj).Error

}

func (obj *K8sCronjob) FillDefaultData() error {

	obj.Commands = common.GenStringArrayByFormKvOnlyKey(obj.CommandsFront)
	obj.Args = common.GenStringArrayByFormKvOnlyKey(obj.ArgsFront)

	return nil
}

func (obj *K8sCronjob) DeleteOne() error {

	return Db.Select(clause.Associations).Unscoped().Delete(obj).Error

}

func GetK8sCronjobAll() (objs []*K8sCronjob, err error) {
	err = Db.Find(&objs).Error
	return
}

func (obj *K8sCronjob) UpdateFlowNodes(nodes []FlowNode) error {

	return Db.Model(obj).Association("FlowNodes").Replace(nodes)

}

func (obj *K8sCronjob) FillFrontAllData() error {
	dbUser, _ := GetUserById(int(obj.UserID))
	if dbUser != nil {
		obj.CreateUserName = dbUser.Username
	}
	// 找到所属项目
	dbProject, err := GetK8sProjectById(int(obj.K8sProjectID))
	if err != nil {
		return err
	}
	obj.ProjectObj = dbProject
	obj.K8sProjectName = dbProject.Name

	// 填充cluster对象

	dbCluster, err := GetK8sClusterByName(obj.ProjectObj.Cluster)
	if err != nil {
		return err
	}
	obj.ClusterObj = dbCluster

	obj.Key = fmt.Sprintf("%d", obj.ID)
	//把 服务树节点对象查询出来
	dbNode, err := GetStreeNodeById(int(obj.TreeNodeId))
	if err != nil {
		return err
	}

	dbNode.FillFrontAllDataSimple()
	obj.StreeNodeObj = dbNode
	obj.NodePath = dbNode.NodePath

	obj.CommandsFront = common.GenFormKvFromStringArrayOnlyKey(obj.Commands)
	obj.ArgsFront = common.GenFormKvFromStringArrayOnlyKey(obj.Args)

	return nil
}

func GetK8sCronjobCount() int {
	var count int64
	Db.Table("K8sCronjobes").Count(&count)
	return int(count)
}

func GetK8sCronjobAllWithLimitOffset(limit, offset int) (objs []*K8sCronjob, err error) {
	err = Db.Preload("FlowNodes").Limit(limit).Offset(offset).Find(&objs).Error
	return
}

func GetK8sCronjobByClusterAndProjectId(projectId int) (objs []*K8sCronjob, err error) {
	err = Db.Where("k8s_project_id= ?", projectId).Find(&objs).Error
	return
}

func GetK8sCronjobByLevel(level int) (objs []*K8sCronjob, err error) {
	err = Db.Where("level = ?", level).Preload("OpsAdmins").Find(&objs).Error
	return
}

// select * from ecs where
func GetK8sCronjobByIdsWithLimitOffset(ids []int, limit, offset int) (objs []*K8sCronjob, err error) {
	err = Db.Limit(limit).Offset(offset).Find(&objs, ids).Error
	return
}

func GetK8sCronjobByInstanceId(instanceId string) (*K8sCronjob, error) {

	var dbObj K8sCronjob
	err := Db.Where("instance_id = ?", instanceId).Preload("FlowNodes").First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("K8sCronjob不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}

func GetK8sCronjobById(id int) (*K8sCronjob, error) {

	var dbObj K8sCronjob
	err := Db.Where("id = ?", id).First(&dbObj).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("K8sCronjob不存在")
		}
		return nil, fmt.Errorf("数据库错误:%w", err)
	}
	return &dbObj, nil

}
